import { AsArray } from '../../interface';
import { isAsync, isFunc, isPromise } from '../../utils';
import { IHookCallItem } from '../interface/i-hook-call-item';
import { SyncSeriesHook } from '../sync-series-hook/sync-series-hook';

/**
 * 串联钩子，按照注册顺序执行
 *
 * @export
 * @class AsyncSeriesHook
 * @template T 输入值类型
 * @template C 上下文声明对象
 */
export class AsyncSeriesHook<T, C = null> extends SyncSeriesHook<T, C> {
  /**
   * 触发钩子，当某个钩子返回「null」时，中断后续执行。
   *
   * @param {C} context
   * @param {...AsArray<T>} args
   * @return {*}  {Promise<C>}
   * @memberof AsyncSeriesHook
   */
  async call(context: C, ...args: AsArray<T>): Promise<C> {
    const _context: C = context || Object.create({});
    let obj: any;
    for (let i = 0; i < this.items.length; i++) {
      const item = this.items[i];
      if (item.fn) {
        if (item.mode === 'promise') {
          obj = item.fn(_context, ...args);
          if (isPromise(obj)) {
            // eslint-disable-next-line no-await-in-loop
            obj = await obj;
          }
        } else {
          obj = item.fn(_context, ...args);
        }
      }
      if (obj === null) {
        console.warn(
          `因${
            item.fn ? item.fn.name : null
          }方法返回值为「null」，钩子中断执行。`,
          item,
        );
        break;
      }
    }
    return _context;
  }

  /**
   * 注册异步钩子
   *
   * @param {((...args: AsArray<T>) => Promise<void>)} fn 回调
   * @memberof AsyncSeriesHook
   */
  tapPromise(fn: (context: C, ...args: AsArray<T>) => Promise<void>): void {
    if (fn && (isFunc(fn) || isAsync(fn))) {
      const opt: IHookCallItem = {
        mode: 'promise',
      };
      opt.fn = fn as any;
      this.items.push(opt);
    }
  }

  /**
   * 删除异步钩子注册
   *
   * @author chitanda
   * @date 2021-04-30 14:04:42
   * @param {(context: C, ...args: AsArray<T>) => Promise<void>} callBack
   */
  removeTapPromise(
    callBack: (context: C, ...args: AsArray<T>) => Promise<void>,
  ): void {
    this.removeTap(callBack);
  }
}
